﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AAA.Meta.Quote.Data;
using AAA.Meta.Chart.Data;

namespace AAA.Meta.Chart.Data
{
    public class ProfileData
    {
        public const string CHART_LABEL = "ABCDEFGHIJKLMNPQRSTUVWXYabcdefghijklmnopqrstuvwxyz";
        private DateTime _startTime;
        private int _iTimePeriod = 15 * 60;
        private float _fPriceInterval;
        private List<DateTime> _lstPeriod;
        private Dictionary<DateTime, ProfilePeriod> _dicProfilePeriod;
        private bool _blCombined = false;

        public ProfileData()
        {
            try
            {
                _startTime = DateTime.Parse(DateTime.Now.ToString("yyyy/MM/dd") + " 08:45:00");
                _lstPeriod = new List<DateTime>();
                _dicProfilePeriod = new Dictionary<DateTime, ProfilePeriod>();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
        }

        public bool IsCombined
        {
            get {
                    _blCombined = false;
                    try {
                        string preDate = "";
                        foreach(DateTime dtKey  in _lstPeriod) {
                            if (preDate == "")
                            {
                                preDate = dtKey.ToString("yyyy/MM/dd");
                            }
                            if (preDate != dtKey.ToString("yyyy/MM/dd")) {
                                _blCombined = true;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message.ToString());
                    }
                    return _blCombined; 
            }
            //set { _blCombined = value; }
        }

        public float Highest
        {
            get {
                float fHighest = -1;
                try {
                    foreach (DateTime _DateTimeKey in _dicProfilePeriod.Keys)
                    {
                        fHighest = Math.Max(fHighest, _dicProfilePeriod[_DateTimeKey].PeriodHighest);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message.ToString());
                }
                return fHighest; 
            }
        }

        public float Lowest
        {
            get {
                float fLowest = 99999999;
                try
                {
                    foreach (DateTime _DateTimeKey in _dicProfilePeriod.Keys)
                    {
                        fLowest = Math.Min(fLowest, _dicProfilePeriod[_DateTimeKey].PeriodLowest);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message.ToString());
                }
                return fLowest;
            }
        }

        public float Open
        {
            get { 
                    float fResult = 0;
                    try
                    {
                        fResult = _dicProfilePeriod[_lstPeriod[0]].PeriodOpen; 
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message.ToString());
                    }
                    return fResult; 
            }
        }

        public float Close
        {
            get {
                    float fResult = 0;
                    try
                    {
                        fResult = _dicProfilePeriod[_lstPeriod[_lstPeriod.Count - 1]].PeriodClose;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message.ToString());
                    }
                    return fResult; 
            }
        }

        public int Volume
        {
            get {   
                    int iVolume = 0;
                    try {
                        foreach(DateTime dtKey in _lstPeriod){
                            iVolume = iVolume + _dicProfilePeriod[dtKey].PeriodVolume;
                        }
                                    }
                    catch (Exception ex) {
                        Console.WriteLine(ex.Message.ToString());
                    }
                    return iVolume; 
            }

        }

        public int Ticks
        {
            get
            {
                int iTicks = 0;
                try{
                    foreach (DateTime dtKey in _lstPeriod)
                    {
                        iTicks = iTicks + _dicProfilePeriod[dtKey].PeriodTicks;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message.ToString());
                }
                return iTicks; 
            }
        }

        public DateTime StartTime
        {
            get { return _startTime; }
            set { _startTime = value; }
        }

        public DateTime EndTime
        {
            get {
                DateTime dtResult = new DateTime();
                try
                {
                        dtResult =  _dicProfilePeriod[_lstPeriod[_lstPeriod.Count - 1]].EndTime;
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.Message.ToString());
                }
                return dtResult;
            }
        }

        public float PriceInterval
        {
            get { return _fPriceInterval; }
            set { _fPriceInterval = value; }
        }

        public int TimePeriod
        {
            get { return _iTimePeriod; }
            set { _iTimePeriod = value; }
        }

        public List<float> PriceList
        {
            get {
                List<float> lstPrice = new List<float>();
                foreach (DateTime dtKey in _lstPeriod)
                {
                    foreach (float fPrice in _dicProfilePeriod[dtKey].PriceList)
                    {
                        if (lstPrice.IndexOf(fPrice) < 0) {
                            lstPrice.Add(fPrice);
                        }
                    }
                }
                lstPrice.Sort();
                return lstPrice; }
        }

        public ProfilePeriod PeriodAtTime(DateTime dtKey) {
            return _dicProfilePeriod[dtKey];
        }

        public List<ProfileCharData> TPOCharList(float fPrice)
        {
            List<ProfileCharData> lstCharProfile = new List<ProfileCharData>();
            string strError = "";
            try
            {
                int intCharIndex = 0;
                foreach (DateTime dtKey in _lstPeriod)
                {
                    strError = fPrice.ToString() + ":" + dtKey.ToString();
                    if (_dicProfilePeriod[dtKey].PriceList.IndexOf(fPrice) >= 0)
                    {
                        ProfileCharData pCharData = new ProfileCharData();
                        pCharData.PeriodKey = dtKey;
                        pCharData.ProfileKey = StartTime;
                        if (dtKey.Ticks - StartTime.Ticks >= 0)
                        {
                            intCharIndex = (int)(new TimeSpan(dtKey.Ticks - CharStartDate().Ticks).TotalSeconds) / TimePeriod;
                        }
                        else {
                            intCharIndex = 0;
                        }
                        intCharIndex = intCharIndex % CHART_LABEL.Length;
                        string strShowChar = CHART_LABEL.Substring(intCharIndex, 1);
                        if ((fPrice == PriceLabel(Open)) && (dtKey.Ticks == StartTime.Ticks))
                        {
                            strShowChar = "O";
                            pCharData.IsOpen = true;
                        }
                        if ((fPrice == PriceLabel(Close)) && (dtKey.Ticks == GetPeriodStartTime(EndTime).Ticks))
                        {
                            strShowChar = "Z";
                            pCharData.IsClose = true;
                        }
                        if (fPrice == PriceLabel(_dicProfilePeriod[dtKey].PeriodOpen))
                        {
                            pCharData.IsOpen = true;
                        }
                        if (IsCombined)
                        {
                            strShowChar = "X";
                        }
                        pCharData.StrChar = strShowChar;
                        lstCharProfile.Add(pCharData);
                    }
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString() + strError);
            }
            return lstCharProfile;
        }

        public float VolumeAtPrice(float fPrice)
        {
            return VolumeAtPrice(fPrice, StartTime, EndTime);
        }

        public float VolumeAtPrice(float fPrice, DateTime VStartTime, DateTime VEndTime)
        {
            float Volume = 0;
            foreach (DateTime dtKey in _lstPeriod)
            {
                if ((VStartTime.Ticks <= dtKey.Ticks) && (VEndTime.Ticks >= dtKey.Ticks))
                {
                    Volume = Volume + _dicProfilePeriod[dtKey].VolumeAtPrice(fPrice);
                }
            }
            return Volume;
        }

        public int TickAtPrice(float fPrice)
        {
            return TickAtPrice(fPrice, StartTime, EndTime);
        }

        public int TickAtPrice(float fPrice, DateTime VStartTime, DateTime VEndTime)
        {
            int Ticks = 0;
            foreach (DateTime dtKey in _lstPeriod)
            {
                if ((VStartTime.Ticks <= dtKey.Ticks) && (VEndTime.Ticks >= dtKey.Ticks))
                {
                    Ticks = Ticks + _dicProfilePeriod[dtKey].TickAtPrice(fPrice);
                }
            }
            return Ticks;
        }

        public float PriceLabel(float fPrice)
        {
            return (float)(Math.Floor(fPrice / PriceInterval) * PriceInterval);
        }

        public void AddData(DateTime dtTime, float fPrice, int iVolume)
        {
            AddData(dtTime, fPrice, iVolume, 1);
        }

        public void AddData(DateTime dtTime, float fPrice, int iVolume, int iTick)
        {
            try
            {
                DateTime tickStartTime = GetPeriodStartTime(dtTime);
                if (!_dicProfilePeriod.ContainsKey(tickStartTime)) {
                    ProfilePeriod newProfilePeriod = new ProfilePeriod();
                    newProfilePeriod.PriceInterval = PriceInterval;
                    newProfilePeriod.TimePeriod = TimePeriod;
                    _dicProfilePeriod.Add(tickStartTime, newProfilePeriod);
                    _dicProfilePeriod[tickStartTime].StartTime = tickStartTime;
                    _lstPeriod.Add(tickStartTime);
                    _lstPeriod.Sort();
                }
                _dicProfilePeriod[tickStartTime].AddData(dtTime, fPrice, iVolume, iTick);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
        }

        private DateTime GetPeriodStartTime(DateTime dtTime)
        {
            TimeSpan timeSpan = new TimeSpan(dtTime.Ticks - StartTime.Ticks);
            DateTime dtResult = StartTime.AddSeconds(Math.Floor((timeSpan.TotalSeconds) / TimePeriod) * TimePeriod);
            return dtResult;
        }

        public int POCTPOCount()
        {
            int iPOCTPOCount = 0;
            foreach (float fPrice in PriceList) {
                iPOCTPOCount = Math.Max(iPOCTPOCount, TPOCharList(fPrice).Count);
            }
            return iPOCTPOCount;
        }

        public float POCVolume()
        {
            float iPOCVolume = 0;
            foreach (float fPrice in PriceList)
            {
                iPOCVolume = Math.Max(iPOCVolume, VolumeAtPrice(fPrice));
            }
            return iPOCVolume;
        }

        public int POCTicks()
        {
            int iPOCTicks = 0;
            try
            {
                foreach (float fPrice in PriceList)
                {
                    iPOCTicks = Math.Max(iPOCTicks, TickAtPrice(fPrice));
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
            return iPOCTicks;
        }

        public void AddPeriod(ProfilePeriod newPeriod) {
            try
            {
                _lstPeriod.Add(newPeriod.StartTime);
                _dicProfilePeriod.Add(newPeriod.StartTime, newPeriod);
                _lstPeriod.Sort();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
        }

        public ProfileData Split(DateTime PeriodIndex) {
            //IsCombined = true;
            ProfileData NewProfile = new ProfileData();
            try
            {
                NewProfile.StartTime = PeriodIndex.AddSeconds(TimePeriod);
                NewProfile.PriceInterval = PriceInterval;
                NewProfile.TimePeriod = TimePeriod;
                //NewProfile.IsCombined = true;
                List<DateTime> oldPeriodList = new List<DateTime>();
                foreach (DateTime dtKey in _lstPeriod)
                {
                    oldPeriodList.Add(dtKey);
                }
                foreach (DateTime dtKey in oldPeriodList) {
                    if (dtKey > PeriodIndex)
                    {
                        NewProfile.AddPeriod(_dicProfilePeriod[dtKey]);
                        _dicProfilePeriod.Remove(dtKey);
                        _lstPeriod.Remove(dtKey);
                    }
                }
                _lstPeriod.Sort();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
            return NewProfile;
        }

        public void Merge(ProfileData newProfile) {
            try {
                if (newProfile.StartTime < StartTime) {
                    StartTime = newProfile.StartTime;
                }
                foreach (DateTime dtKey in newProfile._lstPeriod) {
                    _lstPeriod.Add(dtKey);
                    _dicProfilePeriod.Add(dtKey, newProfile.PeriodAtTime(dtKey));
                }
                _lstPeriod.Sort();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
        }

        public void UpdateOpenClosePrice(List<BarRecord> lstBarRecord) {
            try {
                List<DateTime> lstPeriodBarRecord = new List<DateTime>();
                Dictionary<DateTime, List<BarRecord>> dicBarRecord = new Dictionary<DateTime, List<BarRecord>>();
                foreach (BarRecord barRecord in lstBarRecord) {
                    DateTime dtKey = GetPeriodStartTime(barRecord.BarDateTime);
                    if (!dicBarRecord.ContainsKey(dtKey)) {
                        lstPeriodBarRecord.Add(dtKey);
                        dicBarRecord.Add(dtKey, new List<BarRecord>());
                    }
                    dicBarRecord[dtKey].Add(barRecord);
                }
                foreach (DateTime dtKey in lstPeriodBarRecord) {
                    _dicProfilePeriod[dtKey].UpdateOpenClosePrice(dicBarRecord[dtKey]);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
        }

        public float GetLastPrice()
        {
            float _LastPrice = 0;
            try {
                if (_lstPeriod.Count > 0)
                {
                    if (_dicProfilePeriod[_lstPeriod[_lstPeriod.Count - 1]].PriceList.Count > 0)
                    {
                        _LastPrice = _dicProfilePeriod[_lstPeriod[_lstPeriod.Count - 1]].LastPrice;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
            return _LastPrice;
        }

        private DateTime CharStartDate() {
            return DateTime.Parse( _startTime.ToString("yyyy/MM/dd") + " 08:45:00");
        }

        public List<float> GetMaxVolumePrice() {
            List<float> lstResult = new List<float>();
            float fMaxPrice = -1;
            float fMaxVT = -1;
            try
            {
                for (int i = 0; i < PriceList.Count; i++)
                {
                    float fVT = VolumeAtPrice(PriceList[i]);
                    if (fVT > fMaxVT) {
                        fMaxPrice = PriceList[i];
                        fMaxVT = fVT; 
                    }
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
            lstResult.Add(fMaxPrice);
            lstResult.Add(fMaxVT);
            return lstResult;
        }

        public List<float> GetMaxTickPrice()
        {
            List<float> lstResult = new List<float>();
            float fMaxPrice = -1;
            float fMaxVT = -1;
            try
            {
                for (int i = 0; i < PriceList.Count; i++)
                {
                    float fVT = TickAtPrice(PriceList[i]);
                    if (fVT > fMaxVT)
                    {
                        fMaxPrice = PriceList[i];
                        fMaxVT = fVT;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
            lstResult.Add(fMaxPrice);
            lstResult.Add(fMaxVT);
            return lstResult;
        }

        public List<float> GetProfileValueArea() {
            List<float> lstResult = new List<float>();
            float fVASum = 0;
            int iVAPriceIndex = 0;
            int iVAPriceUpperIndex = 0;
            int iVAPriceLowerIndex = 0;
            for (int i = 0; i < PriceList.Count; i++)
            {
                if (fVASum < VolumeAtPrice(PriceList[i])) {
                    fVASum = VolumeAtPrice(PriceList[i]);
                    iVAPriceIndex = i;
                    iVAPriceUpperIndex = i + 1;
                    iVAPriceLowerIndex = i - 1;
                }
            }
            float fVATemp = 0;
            if (Volume != 0) {
                while (fVASum / Volume < 0.7) { 
                    if (iVAPriceUpperIndex <= PriceList.Count - 1) {
                        fVATemp = VolumeAtPrice(PriceList[iVAPriceUpperIndex]);
                        if ((iVAPriceLowerIndex >= 0) && (fVATemp < VolumeAtPrice(PriceList[iVAPriceLowerIndex]))) {
                            fVATemp = VolumeAtPrice(PriceList[iVAPriceLowerIndex]);
                            iVAPriceLowerIndex--;
                        }
                        else {
                            iVAPriceUpperIndex++;
                        }
                    }
                    else if (iVAPriceLowerIndex >= 0) {
                        fVATemp = VolumeAtPrice(PriceList[iVAPriceLowerIndex]);
                        iVAPriceLowerIndex--;
                    }
                    fVASum = fVASum + fVATemp;
                }
                lstResult.Add(PriceList[Math.Max(0,iVAPriceLowerIndex)]);
                lstResult.Add(PriceList[Math.Min(iVAPriceUpperIndex, PriceList.Count - 1)]);
            }
            return lstResult;
        }

        public Dictionary<float, List<int>> GetPOCShiftList()
        {
            Dictionary<float, List<int>> lstResult = new Dictionary<float, List<int>>();
            try
            {
                int intCharIndex = 0;
                foreach (DateTime dtKey in _lstPeriod)
                {

                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
            return lstResult;
        }
    }
}
